<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>nodejs + koa2 + ts + mysql + redis + vue 重构若依后台管理系统角色权限设计 | 云游</title>
    <meta name="generator" content="VuePress 1.9.7">
    <link rel="icon" href="favicon.ico">
    <script src="/live2dw/L2Dwidget.min.js"></script>
    <script src="/live2dw/L2Dwidget.0.min.js"></script>
    <script src="/live2dw/initLine2dw.js"></script>
    <link rel="stylesheet" href="/live2dw/initLine2dw.css">
    <meta name="description" content="仰天大笑出门去">
    <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
    
    <link rel="preload" href="/assets/css/0.styles.746f5c46.css" as="style"><link rel="preload" href="/assets/js/app.8ca67ad1.js" as="script"><link rel="preload" href="/assets/js/3.dc5e7bc7.js" as="script"><link rel="preload" href="/assets/js/1.f19b5812.js" as="script"><link rel="preload" href="/assets/js/42.b0eb455d.js" as="script"><link rel="prefetch" href="/assets/js/10.063f4d84.js"><link rel="prefetch" href="/assets/js/11.7aaafe6d.js"><link rel="prefetch" href="/assets/js/12.1d3cbcb7.js"><link rel="prefetch" href="/assets/js/13.3eff3441.js"><link rel="prefetch" href="/assets/js/14.d011e66e.js"><link rel="prefetch" href="/assets/js/15.64da65a0.js"><link rel="prefetch" href="/assets/js/16.afe5f3d7.js"><link rel="prefetch" href="/assets/js/17.9645712f.js"><link rel="prefetch" href="/assets/js/18.8acac1b3.js"><link rel="prefetch" href="/assets/js/19.211aa7fa.js"><link rel="prefetch" href="/assets/js/20.33c40850.js"><link rel="prefetch" href="/assets/js/21.a5f61bc3.js"><link rel="prefetch" href="/assets/js/22.3f31af86.js"><link rel="prefetch" href="/assets/js/23.c1a2f360.js"><link rel="prefetch" href="/assets/js/24.f92207d5.js"><link rel="prefetch" href="/assets/js/25.3859fc8e.js"><link rel="prefetch" href="/assets/js/26.c3bb43ea.js"><link rel="prefetch" href="/assets/js/27.2d3ac28b.js"><link rel="prefetch" href="/assets/js/28.5eba4e00.js"><link rel="prefetch" href="/assets/js/29.591a72c1.js"><link rel="prefetch" href="/assets/js/30.4b4e3dae.js"><link rel="prefetch" href="/assets/js/31.0c3aac1f.js"><link rel="prefetch" href="/assets/js/32.927b230d.js"><link rel="prefetch" href="/assets/js/33.4ecffa3d.js"><link rel="prefetch" href="/assets/js/34.b9bc1b48.js"><link rel="prefetch" href="/assets/js/35.42f94296.js"><link rel="prefetch" href="/assets/js/36.95b6cc33.js"><link rel="prefetch" href="/assets/js/37.f3505924.js"><link rel="prefetch" href="/assets/js/38.19c4c627.js"><link rel="prefetch" href="/assets/js/39.592f8fae.js"><link rel="prefetch" href="/assets/js/4.47f43f55.js"><link rel="prefetch" href="/assets/js/40.e4393725.js"><link rel="prefetch" href="/assets/js/41.66b5758c.js"><link rel="prefetch" href="/assets/js/43.11fab467.js"><link rel="prefetch" href="/assets/js/44.cd6e5a73.js"><link rel="prefetch" href="/assets/js/45.b3e3b872.js"><link rel="prefetch" href="/assets/js/46.07a07f51.js"><link rel="prefetch" href="/assets/js/47.c2287444.js"><link rel="prefetch" href="/assets/js/48.66123573.js"><link rel="prefetch" href="/assets/js/49.bebbe116.js"><link rel="prefetch" href="/assets/js/5.e8c05b51.js"><link rel="prefetch" href="/assets/js/50.70436f90.js"><link rel="prefetch" href="/assets/js/51.18985c0f.js"><link rel="prefetch" href="/assets/js/52.ace084da.js"><link rel="prefetch" href="/assets/js/53.02616c91.js"><link rel="prefetch" href="/assets/js/54.6eb33990.js"><link rel="prefetch" href="/assets/js/55.733d5e7a.js"><link rel="prefetch" href="/assets/js/56.36ca4523.js"><link rel="prefetch" href="/assets/js/57.80493e39.js"><link rel="prefetch" href="/assets/js/58.34b95d6c.js"><link rel="prefetch" href="/assets/js/59.96be3833.js"><link rel="prefetch" href="/assets/js/6.027fb3c9.js"><link rel="prefetch" href="/assets/js/60.214c4180.js"><link rel="prefetch" href="/assets/js/61.ba0ebff0.js"><link rel="prefetch" href="/assets/js/62.09089206.js"><link rel="prefetch" href="/assets/js/63.a5c5aa3e.js"><link rel="prefetch" href="/assets/js/64.2d5fc9ca.js"><link rel="prefetch" href="/assets/js/65.c8171d3c.js"><link rel="prefetch" href="/assets/js/66.6d4bbf9e.js"><link rel="prefetch" href="/assets/js/67.ceeafab4.js"><link rel="prefetch" href="/assets/js/68.16d3218f.js"><link rel="prefetch" href="/assets/js/7.b5f855a8.js"><link rel="prefetch" href="/assets/js/8.5ac3177c.js"><link rel="prefetch" href="/assets/js/9.8e7f0e06.js">
    <link rel="stylesheet" href="/assets/css/0.styles.746f5c46.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container no-sidebar" data-v-1aefc0b4><div data-v-1aefc0b4><div id="loader-wrapper" class="loading-wrapper" data-v-d48f4d20 data-v-1aefc0b4 data-v-1aefc0b4><div class="loader-main" data-v-d48f4d20><div data-v-d48f4d20></div><div data-v-d48f4d20></div><div data-v-d48f4d20></div><div data-v-d48f4d20></div></div> <!----> <!----></div> <div class="password-shadow password-wrapper-out" style="display:none;" data-v-25ba6db2 data-v-1aefc0b4 data-v-1aefc0b4><h3 class="title" data-v-25ba6db2 data-v-25ba6db2>云游</h3> <p class="description" data-v-25ba6db2 data-v-25ba6db2>仰天大笑出门去</p> <label id="box" class="inputBox" data-v-25ba6db2 data-v-25ba6db2><input type="password" value="" data-v-25ba6db2> <span data-v-25ba6db2>Konck! Knock!</span> <button data-v-25ba6db2>OK</button></label> <div class="footer" data-v-25ba6db2 data-v-25ba6db2><span data-v-25ba6db2><i class="iconfont reco-theme" data-v-25ba6db2></i> <a target="blank" href="https://vuepress-theme-reco.recoluan.com" data-v-25ba6db2>vuePress-theme-reco</a></span> <span data-v-25ba6db2><i class="iconfont reco-copyright" data-v-25ba6db2></i> <a data-v-25ba6db2><span data-v-25ba6db2>fhtwl</span>
            
          <!---->
          2022
        </a></span></div></div> <div class="hide" data-v-1aefc0b4><header class="navbar" data-v-1aefc0b4><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><!----> <span class="site-name">云游</span></a> <div class="links"><div class="color-picker"><a class="color-button"><i class="iconfont reco-color"></i></a> <div class="color-picker-menu" style="display:none;"><div class="mode-options"><h4 class="title">Choose mode</h4> <ul class="color-mode-options"><li class="dark">dark</li><li class="auto active">auto</li><li class="light">light</li></ul></div></div></div> <div class="search-box"><i class="iconfont reco-search"></i> <input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/" class="nav-link"><i class="iconfont reco-home"></i>
  指南
</a></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><i class="iconfont reco-category"></i>
      分类
    </span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/categories/前端/" class="nav-link"><i class="undefined"></i>
  前端
</a></li><li class="dropdown-item"><!----> <a href="/categories/后端/" class="nav-link"><i class="undefined"></i>
  后端
</a></li><li class="dropdown-item"><!----> <a href="/categories/其它/" class="nav-link"><i class="undefined"></i>
  其它
</a></li></ul></div></div><div class="nav-item"><a href="/tag/" class="nav-link"><i class="iconfont reco-tag"></i>
  标签
</a></div><div class="nav-item"><a href="/timeLine/" class="nav-link"><i class="iconfont reco-date"></i>
  时间线
</a></div><div class="nav-item"><a href="https://github.com/fhtwl/fhtwl.github.io" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-github"></i>
  GitHub
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class="sidebar-mask" data-v-1aefc0b4></div> <aside class="sidebar" data-v-1aefc0b4><div class="personal-info-wrapper" data-v-39576ba9 data-v-1aefc0b4><!----> <h3 class="name" data-v-39576ba9>
    fhtwl
  </h3> <div class="num" data-v-39576ba9><div data-v-39576ba9><h3 data-v-39576ba9>57</h3> <h6 data-v-39576ba9>Articles</h6></div> <div data-v-39576ba9><h3 data-v-39576ba9>64</h3> <h6 data-v-39576ba9>Tags</h6></div></div> <ul class="social-links" data-v-39576ba9></ul> <hr data-v-39576ba9></div> <nav class="nav-links"><div class="nav-item"><a href="/" class="nav-link"><i class="iconfont reco-home"></i>
  指南
</a></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><i class="iconfont reco-category"></i>
      分类
    </span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/categories/前端/" class="nav-link"><i class="undefined"></i>
  前端
</a></li><li class="dropdown-item"><!----> <a href="/categories/后端/" class="nav-link"><i class="undefined"></i>
  后端
</a></li><li class="dropdown-item"><!----> <a href="/categories/其它/" class="nav-link"><i class="undefined"></i>
  其它
</a></li></ul></div></div><div class="nav-item"><a href="/tag/" class="nav-link"><i class="iconfont reco-tag"></i>
  标签
</a></div><div class="nav-item"><a href="/timeLine/" class="nav-link"><i class="iconfont reco-date"></i>
  时间线
</a></div><div class="nav-item"><a href="https://github.com/fhtwl/fhtwl.github.io" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-github"></i>
  GitHub
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div> <!----></nav> <!----> </aside> <div class="password-shadow password-wrapper-in" style="display:none;" data-v-25ba6db2 data-v-1aefc0b4><h3 class="title" data-v-25ba6db2 data-v-25ba6db2>nodejs + koa2 + ts + mysql + redis + vue 重构若依后台管理系统角色权限设计</h3> <!----> <label id="box" class="inputBox" data-v-25ba6db2 data-v-25ba6db2><input type="password" value="" data-v-25ba6db2> <span data-v-25ba6db2>Konck! Knock!</span> <button data-v-25ba6db2>OK</button></label> <div class="footer" data-v-25ba6db2 data-v-25ba6db2><span data-v-25ba6db2><i class="iconfont reco-theme" data-v-25ba6db2></i> <a target="blank" href="https://vuepress-theme-reco.recoluan.com" data-v-25ba6db2>vuePress-theme-reco</a></span> <span data-v-25ba6db2><i class="iconfont reco-copyright" data-v-25ba6db2></i> <a data-v-25ba6db2><span data-v-25ba6db2>fhtwl</span>
            
          <!---->
          2022
        </a></span></div></div> <div data-v-1aefc0b4><main class="page" style="padding-right:0;"><section><div class="page-title"><h1 class="title">nodejs + koa2 + ts + mysql + redis + vue 重构若依后台管理系统角色权限设计</h1> <div data-v-f875f3fc><i class="iconfont reco-account" data-v-f875f3fc><span data-v-f875f3fc>fhtwl</span></i> <i class="iconfont reco-date" data-v-f875f3fc><span data-v-f875f3fc>4/25/2022</span></i> <i class="iconfont reco-eye" data-v-f875f3fc><span id="/blog/nodejs/3.html" data-flag-title="Your Article Title" class="leancloud-visitors" data-v-f875f3fc><a class="leancloud-visitors-count" style="font-size:.9rem;font-weight:normal;color:#999;"></a></span></i> <i class="tags iconfont reco-tag" data-v-f875f3fc><span class="tag-item" data-v-f875f3fc>nodejs</span><span class="tag-item" data-v-f875f3fc>vue.js</span><span class="tag-item" data-v-f875f3fc>mysql</span><span class="tag-item" data-v-f875f3fc>redis</span><span class="tag-item" data-v-f875f3fc>koa2</span></i></div></div> <div class="theme-reco-content content__default"><h1 id="前言"><a href="#前言" class="header-anchor">#</a> 前言</h1> <p>具有用户、角色、权限等概念的系统、网站或者项目, 抛开业务功能, 它们本质上都有一个角色权限系统, 这是系统的地基.</p> <p>若依的权限管理系统, 主要有角色管理、菜单管理和用户管理3部分, 每个用户都有自己的角色, 每个角色都有自己的菜单和按钮权限, 这是用户看得到的部分.在用户看不到的地方, 还有角色的接口权限.</p> <p>在了解若依权限逻辑后, 用koa2 + Ts和vue + Antdv重构了其角色权限系统</p> <h1 id="项目开发"><a href="#项目开发" class="header-anchor">#</a> 项目开发</h1> <h2 id="_1-前端"><a href="#_1-前端" class="header-anchor">#</a> 1. 前端</h2> <p>前端部分我主要是使用antdv pro去实现.
主要有token, 动态路由, v-action(控制按钮显示隐藏的自定义指令), 具体内容可以参考<a href="https://pro.antdv.com/docs/authority-management">Antdv Pro官网</a></p> <h2 id="_2-后端"><a href="#_2-后端" class="header-anchor">#</a> 2. 后端</h2> <h3 id="数据库设计"><a href="#数据库设计" class="header-anchor">#</a> 数据库设计</h3> <ol><li>mysql</li></ol> <p>数据库建表主要有角色表role, 菜单表menu, 用户表user等.</p> <ul><li>每一个用户user都有一个角色id <b>role_id</b>;</li> <li>每一个角色role都有一个权限的集合字段 <b>permissions</b>, 这个集合里包含该角色拥有的所有权限id(以逗号分隔的id字符串);</li> <li>每个角色都拥有父级角色 <b>parent_id</b>, 角色的权限可继承, 即每个角色的权限等于自己权限和自己所有祖宗角色权限的并集;</li> <li>角色的权限体现在菜单分类、菜单、按钮的操作权限和接口的访问权限上, 这些都保存在菜单表menu的记录里, 角色role的权限的集合字段 <b>permissions</b>的数据即是多条menu的<b>id</b>的集合, 通过 <b>type</b>字段区分菜单分类、菜单和按钮, 每个菜单(即页面)和按钮都有自己的权限标识 <b>permission</b>, 通过权限标识来控制菜单的接口和按钮的接口访问权限, 以及按钮在前端的操作权限;</li> <li>menu表的记录分为分类、菜单和按钮, 每个menu都有父级 <b>parent_id</b>, 分类的父级只能是分类, 菜单的父级只能是分类, 按钮的父级只能菜单.通过父子关系, 就能组合成改角色的前端路由</li></ul> <ol start="2"><li>redis</li></ol> <p>redis主要用来缓存token和角色</p> <ul><li>token以字符串类型保存, 以token为key, userId作为值</li> <li>角色以hash类型保存, 每一个角色都是一个对象, 角色id作为key, 每个值里有<b>id</b>、<b>parentId</b>, 和<b>permission</b>, 通过id和parentId确定角色的父子关系, 通过permission保存该角色的权限标识, 通过权限标识来判断该该角色是否可以访问某个接口</li></ul> <h3 id="目录介绍"><a href="#目录介绍" class="header-anchor">#</a> 目录介绍</h3> <p><img src="/images/nodejs/目录.jpg"></p>
  src<br>
    + api 存放接口<br>
    + common 公共资源 <br>
      + apiValidate 接口参数json配置, 配置每个接口参数的数据类型等  <br>
      + lib 常量等  <br>
      + typings 类型声明  <br>
      + utils 工具函数  <br>
    + config 系统配置, 数据库配置等 <br>
    + core 核心静态类 <br>
      + HttpException http异常  <br>
      + Init 项目初始化入口 <br>
      + MongoDB MongoDB封装 <br>
      + Mysql Mysql封装 <br>
      + Redis 封装  <br>
    + middlewares 中间件  <br>
      + Auth 用户认证相关中间件, 校验token是否合法, 权限是否满足  <br>
      + code 验证码校验中间件  <br>
      + exception 全局异常监听和处理中间件  <br>
      + logs 日志中间件  <br>
      + security 安全中间件 限制ip访问次数等  <br>
      + upload 文件上传中间件  <br>
      + validator 接口请求参数json校验中间件  <br>
    + public 静态资源目录  <br>
    + service 公共业务  <br>
    + app.ts 入口文件
<h3 id="基础服务搭建"><a href="#基础服务搭建" class="header-anchor">#</a> 基础服务搭建</h3> <h4 id="_1-项目创建"><a href="#_1-项目创建" class="header-anchor">#</a> 1. 项目创建</h4> <ol><li>创建包管理文件</li></ol> <div class="language-sh extra-class"><pre class="language-sh"><code><span class="token comment"># 生成package.json</span>
<span class="token function">npm</span> init -y
</code></pre></div><p>新建src文件夹, 在src根目录新建app.ts文件, app.ts就是整个项目的入口</p> <ol start="2"><li>安装依赖</li></ol> <div class="language-sh extra-class"><pre class="language-sh"><code><span class="token comment"># 依赖</span>
<span class="token function">npm</span> i koa koa-bodyparser koa-router koa-session koa-static koa2-cors log4js mongodb mysql svg-captcha validator ajv ioredis jsonwebtoken
<span class="token comment"># 依赖注解</span>
<span class="token function">npm</span> i --save-dev @types/koa @types/koa-bodyparser @types/koa-router @types/koa-session @types/koa-static @types/koa2-cors @types/log4js @types/mongodb @types/mysql @types/validator @types/ajv @types/ioredis @types/jsonwebtoken

</code></pre></div><ol start="3"><li>初始化ts配置</li></ol> <div class="language-sh extra-class"><pre class="language-sh"><code><span class="token comment"># 生成tsconfig.json</span>
tsc --init
</code></pre></div><p>修改tsconfig.json</p> <div class="language-json extra-class"><pre class="language-json"><code><span class="token comment">// tsconfig.json</span>
<span class="token punctuation">{</span>
  <span class="token property">&quot;compilerOptions&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token property">&quot;target&quot;</span><span class="token operator">:</span> <span class="token string">&quot;ES2015&quot;</span><span class="token punctuation">,</span>  <span class="token comment">// 目标语言版本</span>
    <span class="token property">&quot;module&quot;</span><span class="token operator">:</span> <span class="token string">&quot;commonjs&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 指定生成代码的模板标准</span>
    <span class="token property">&quot;rootDir&quot;</span><span class="token operator">:</span> <span class="token string">&quot;./&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 指定输出目录, 默认是dist文件夹</span>
    <span class="token property">&quot;strict&quot;</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// 严格模式</span>
    <span class="token property">&quot;allowSyntheticDefaultImports&quot;</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// 没有默认导出时, 编译器会创建一个默认导出</span>
    <span class="token property">&quot;esModuleInterop&quot;</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// 允许export= 导出, 由import from导入</span>
    <span class="token property">&quot;forceConsistentCasingInFileNames&quot;</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token comment">// 强制区分大小写 </span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token property">&quot;include&quot;</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token comment">// 需要编译的的文件和目录</span>
    <span class="token string">&quot;src/**/*&quot;</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token property">&quot;files&quot;</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token string">&quot;src/app.ts&quot;</span>
  <span class="token punctuation">]</span>
<span class="token punctuation">}</span>

</code></pre></div><ol start="4"><li>运行项目</li></ol> <p>在app.ts里实例化一个koa服务器</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/app.ts</span>

<span class="token comment">// 引入koa</span>
<span class="token keyword">import</span> Koa <span class="token keyword">from</span> <span class="token string">'koa'</span>
<span class="token keyword">import</span> http <span class="token keyword">from</span> <span class="token string">'http'</span>
<span class="token comment">// 创建koa实例</span>
<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Koa</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 创建服务器</span>
<span class="token keyword">const</span> server<span class="token operator">:</span> http<span class="token punctuation">.</span>Server <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">http</span><span class="token punctuation">.</span><span class="token function">Server</span><span class="token punctuation">(</span>app<span class="token punctuation">.</span><span class="token function">callback</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token comment">// 中间件</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  ctx<span class="token punctuation">.</span>body <span class="token operator">=</span> <span class="token string">'Hello World'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token comment">// 监听端口</span>
app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token number">9000</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'run success'</span><span class="token punctuation">)</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'app started at port 9000...'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre></div><p>命令行输入</p> <div class="language-sh extra-class"><pre class="language-sh"><code><span class="token comment"># 编译ts文件</span>
tsc
<span class="token comment"># 运行编译的文件</span>
<span class="token function">node</span> ./dist/src/app.js
</code></pre></div><p>命令行会打印</p> <div class="language- extra-class"><pre class="language-text"><code>run success
app started at port 9000...
</code></pre></div><p>我们可以在浏览器访问 <a href="http://localhost:9000/">http://localhost:9000/</a> <br>
此时页面上应该会显示 Hello World <br></p> <p>每次都需要执行编译和运行, 太麻烦, 我们可以在修改package.json, 添加dev命令</p> <div class="language-json extra-class"><pre class="language-json"><code><span class="token comment">// package.json</span>
<span class="token punctuation">{</span>
  ...

  <span class="token property">&quot;scripts&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token property">&quot;dev&quot;</span><span class="token operator">:</span> <span class="token string">&quot;tsc &amp;&amp; node ./dist/src/app.js&quot;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  ...
<span class="token punctuation">}</span>

</code></pre></div><p>之后每次运行npm run dev即可 <br>
但是这样依然繁琐, 我们希望可以自动监听文件的保存并自动刷新, 这个可以用nodemon和ts-node来实现 <br>
前者可以监听文件的变更，自动重启服务, 后者可以直接运行ts文件</p> <p>安装nodemon</p> <div class="language-sh extra-class"><pre class="language-sh"><code><span class="token function">npm</span> i nodemon ts-node typescript --save-dev
</code></pre></div><p>修改package.json的dev命令</p> <div class="language-json extra-class"><pre class="language-json"><code><span class="token comment">// package.json</span>
<span class="token punctuation">{</span>

  ...
  <span class="token property">&quot;scripts&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token property">&quot;dev&quot;</span><span class="token operator">:</span> <span class="token string">&quot;nodemon ./src/app.ts&quot;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  ...
<span class="token punctuation">}</span>
</code></pre></div><p>为了控制代码风格和质量, 我们可以引入eslint和prettier</p> <div class="language-sh extra-class"><pre class="language-sh"><code><span class="token function">npm</span> i eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-prettier prettier --save-dev

</code></pre></div><p>在根目录创建 .eslintrc.js</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// .eslintrc.js</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">root</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>

  <span class="token literal-property property">env</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">node</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token literal-property property">es2021</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>

  <span class="token literal-property property">parser</span><span class="token operator">:</span> <span class="token string">'@typescript-eslint/parser'</span><span class="token punctuation">,</span>

  <span class="token literal-property property">parserOptions</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">ecmaVersion</span><span class="token operator">:</span> <span class="token number">12</span><span class="token punctuation">,</span>
    <span class="token literal-property property">sourceType</span><span class="token operator">:</span> <span class="token string">'module'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">tsconfigRootDir</span><span class="token operator">:</span> __dirname<span class="token punctuation">,</span>
    <span class="token literal-property property">project</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'./tsconfig.json'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>

  <span class="token literal-property property">plugins</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'@typescript-eslint'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token literal-property property">rules</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token string-property property">&quot;@typescript-eslint/no-unsafe-assignment&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span>
    <span class="token string-property property">&quot;@typescript-eslint/no-non-null-assertion&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span>
    <span class="token string-property property">&quot;no-useless-escape&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span>
    <span class="token string-property property">&quot;@typescript-eslint/no-unsafe-member-access&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span>
    <span class="token string-property property">&quot;@typescript-eslint/unbound-method&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span>
    <span class="token string-property property">&quot;@typescript-eslint/await-thnable&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span>
    <span class="token string-property property">&quot;@typescript-eslint/restrict-template-expressions&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span>
    <span class="token string-property property">&quot;@typescript-eslint/no-misused-promises&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span>
    <span class="token string-property property">'@typescript-eslint/no-explicit-any'</span><span class="token operator">:</span> <span class="token string">'off'</span><span class="token punctuation">,</span>
    <span class="token string-property property">&quot;@typescript-eslint/no-unsafe-call&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span>
    <span class="token string-property property">&quot;@typescript-eslint/no-unsafe-argument&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span>
    <span class="token string-property property">&quot;no-async-promise-executor&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span>
    <span class="token string-property property">&quot;@typescript-eslint/no-floating-promises&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span>
    <span class="token string-property property">&quot;@typescript-eslint/require-await&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span>
    <span class="token string-property property">&quot;@typescript-eslint/no-var-requires&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span>
    <span class="token string-property property">&quot;@typescript-eslint/ban-types&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span>
    <span class="token string-property property">&quot;no-prototype-builtins&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span>
    <span class="token string-property property">&quot;space-before-function-paren&quot;</span><span class="token operator">:</span> <span class="token number">0</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>

  <span class="token keyword">extends</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token string">'eslint:recommended'</span><span class="token punctuation">,</span>
    <span class="token string">'plugin:@typescript-eslint/recommended'</span><span class="token punctuation">,</span>
    <span class="token string">'plugin:@typescript-eslint/recommended-requiring-type-checking'</span><span class="token punctuation">,</span>
    <span class="token string">'prettier'</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span>


</code></pre></div><p>在根目录创建.prettierrc和.editorconfig</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// .prettierrc</span>
<span class="token punctuation">{</span>
  <span class="token string-property property">&quot;printWidth&quot;</span><span class="token operator">:</span> <span class="token number">120</span><span class="token punctuation">,</span>
  <span class="token string-property property">&quot;semi&quot;</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
  <span class="token string-property property">&quot;singleQuote&quot;</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  <span class="token string-property property">&quot;prettier.spaceBeforeFunctionParen&quot;</span><span class="token operator">:</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span>

</code></pre></div><div class="language-sh extra-class"><pre class="language-sh"><code><span class="token comment"># .editorconfig</span>
root <span class="token operator">=</span> <span class="token boolean">true</span>

<span class="token punctuation">[</span>*.<span class="token punctuation">{</span>js,ts,json<span class="token punctuation">}</span><span class="token punctuation">]</span>
<span class="token assign-left variable">charset</span><span class="token operator">=</span>utf-8
<span class="token assign-left variable">end_of_line</span><span class="token operator">=</span>lf
<span class="token assign-left variable">insert_final_newline</span><span class="token operator">=</span>false
<span class="token assign-left variable">indent_style</span><span class="token operator">=</span>space
trim_trailing_whitespace <span class="token operator">=</span> <span class="token boolean">true</span>
<span class="token assign-left variable">indent_size</span><span class="token operator">=</span><span class="token number">2</span>
insert_final_newline <span class="token operator">=</span> <span class="token boolean">true</span>

</code></pre></div><p>编辑vscode配置文件setting.json，在末尾添加下列行</p> <div class="language-json extra-class"><pre class="language-json"><code><span class="token comment">// setting.json</span>
<span class="token punctuation">{</span>
  ...
  <span class="token property">&quot;eslint.format.enable&quot;</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  <span class="token property">&quot;editor.codeActionsOnSave&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token comment">// 启用ESLint规则格式化以上设为none的代码</span>
      <span class="token property">&quot;source.fixAll.eslint&quot;</span><span class="token operator">:</span> <span class="token boolean">true</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token property">&quot;editor.tabSize&quot;</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>

  <span class="token comment">// 保存时格式化代码</span>
  <span class="token property">&quot;editor.formatOnSave&quot;</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  <span class="token property">&quot;eslint.trace.server&quot;</span><span class="token operator">:</span> <span class="token string">&quot;off&quot;</span><span class="token punctuation">,</span>
  <span class="token comment">// 粘贴时格式化代码</span>
  <span class="token property">&quot;editor.formatOnPaste&quot;</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
  <span class="token property">&quot;[typescript]&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token property">&quot;editor.defaultFormatter&quot;</span><span class="token operator">:</span> <span class="token string">&quot;esbenp.prettier-vscode&quot;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token property">&quot;[markdown]&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token property">&quot;editor.defaultFormatter&quot;</span><span class="token operator">:</span> <span class="token string">&quot;rvest.vs-code-prettier-eslint&quot;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span>

</code></pre></div><p>安装vscode插件Prettier - Code formatter <br></p> <p>修改src/app.ts, 测试是否生效</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/app.ts</span>
<span class="token keyword">let</span> a <span class="token operator">=</span> <span class="token punctuation">{</span>
    num<span class="token operator">:</span> <span class="token number">1</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

</code></pre></div><p>此时eslint会提示有2个警告1个错误, 代码有3处不符合我们设置的规范</p> <ul><li>'a' is never reassigned. Use 'const' instead.</li> <li>num这行应该使用2个空格作为缩进, 而不是4个空格</li> <li>结束行应该没有分号作为结束</li></ul> <p>当我们ctrl+s保存文件, 会发现编辑器将代码修改为</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/app.ts</span>
<span class="token keyword">const</span> a <span class="token operator">=</span> <span class="token punctuation">{</span>
  num<span class="token operator">:</span> <span class="token number">1</span>
<span class="token punctuation">}</span>
</code></pre></div><p>不规范的地方被修复了</p> <ol start="5"><li>项目编译
在正式环境里部署项目, 我们不再需要对文件修改进行监听, 而是直接编译文件. 因此我们添加一个build命令</li></ol> <div class="language-json extra-class"><pre class="language-json"><code><span class="token comment">// package.json</span>
<span class="token punctuation">{</span>

  ...
  <span class="token property">&quot;scripts&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token property">&quot;dev&quot;</span><span class="token operator">:</span> <span class="token string">&quot;nodemon ./src/app.ts&quot;</span><span class="token punctuation">,</span>
    <span class="token property">&quot;build&quot;</span><span class="token operator">:</span> <span class="token string">&quot;tsc &amp;&amp; node ./dist/src/app.js&quot;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  ...
<span class="token punctuation">}</span>
</code></pre></div><p>如果项目是部署window环境下, 直接ctrl+c就可以停止项目, 但是如果在linux环境下, 重启项目就比较麻烦, 可以考虑使用pm2来管理node服务</p> <div class="language-sh extra-class"><pre class="language-sh"><code><span class="token comment"># 全局安装pm2</span>
<span class="token function">npm</span> <span class="token function">install</span> pm2 -g

<span class="token comment"># 项目根目录</span>
<span class="token comment"># 运行项目</span>
<span class="token function">npm</span> run build

<span class="token comment"># 查看项目运行状态</span>
pm2 list

<span class="token comment"># 重启项目</span>
pm2 restart ./dist/src/app.js

</code></pre></div><p>为了区分正式环境和开发环境, 我们可以使用cross-env添加环境变量</p> <div class="language-sh extra-class"><pre class="language-sh"><code><span class="token comment"># 安装cross-env</span>
<span class="token function">npm</span> i cross-env --save-dev
</code></pre></div><p>修改dev和build命令</p> <div class="language-json extra-class"><pre class="language-json"><code><span class="token comment">// package.json</span>
<span class="token punctuation">{</span>
  ...
  <span class="token property">&quot;scripts&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token property">&quot;dev&quot;</span><span class="token operator">:</span> <span class="token string">&quot;cross-env NODE_ENV=development nodemon ./src/app.ts&quot;</span><span class="token punctuation">,</span>
    <span class="token property">&quot;build&quot;</span><span class="token operator">:</span> <span class="token string">&quot;tsc &amp;&amp; cross-env NODE_ENV=production node dist/src/app.js&quot;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  ...
<span class="token punctuation">}</span>

</code></pre></div><p>修改 src/app.ts</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token keyword">import</span> Koa <span class="token keyword">from</span> <span class="token string">&quot;koa&quot;</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> http <span class="token keyword">from</span> <span class="token string">&quot;http&quot;</span><span class="token punctuation">;</span>
<span class="token comment">// 创建koa实例</span>
<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Koa</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 创建服务器</span>
<span class="token keyword">const</span> server<span class="token operator">:</span> http<span class="token punctuation">.</span>Server <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">http</span><span class="token punctuation">.</span><span class="token function">Server</span><span class="token punctuation">(</span>app<span class="token punctuation">.</span><span class="token function">callback</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 中间件</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  ctx<span class="token punctuation">.</span>body <span class="token operator">=</span> <span class="token string">&quot;Hello World&quot;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 监听端口</span>
app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token number">9000</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">&quot;run success&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">&quot;app started at port 9000...&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

</code></pre></div><p>我们就能看到控制台打印出来的development。</p> <p>至此,基础环境搭建基本完成</p> <h4 id="_2-中间件开发"><a href="#_2-中间件开发" class="header-anchor">#</a> 2. 中间件开发</h4> <ol><li>中间件的含义和作用</li></ol> <p>中间件是一种封装方式, 用于处理http请求的功能 <br>
如果将一个http请求比喻为一条运水的管道, 那么中间件就是管道上的仪表、阀门等处理装置 <br>
中间件主要有3个核心概念: 请求request、响应response和next函数 <br>
request和response是请求的上下文信息Context, next函数用于控制状态.  <br>
如果一个请求, 没有设置response, 那么这个请求就会返回404  <br>
如果一个请求, 进入某个中间件后, 只有当执行next函数后, 请求才会继续执行下一个中间件. 如果没有执行next函数, 而是设置response的状态码和返回值, 那么请求就会结束, 不再继续执行下面的中间件，而是直接把response返回给前端. <br></p> <ol start="2"><li>koa如何使用中间件</li></ol> <p>koa使用use方法载入中间件</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// 中间件middleware</span>
<span class="token keyword">const</span> <span class="token function-variable function">middleware1</span> <span class="token operator">=</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">,</span> next<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'1'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// 中间件middleware2</span>
<span class="token keyword">const</span> <span class="token function-variable function">middleware2</span> <span class="token operator">=</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">,</span> next<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'2'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  ctx<span class="token punctuation">.</span>body <span class="token operator">=</span> <span class="token string">'hello world'</span>
<span class="token punctuation">}</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>middleware1<span class="token punctuation">)</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>middleware2<span class="token punctuation">)</span>
</code></pre></div><p>值得注意的是, 中间件先被use的, 就先执行 <br></p> <p>下面我们介绍一些常用的中间件，并开发一些中间件<br></p> <p>为了模块清晰, 我们在src目录下创建core文件夹, 用户存放核心静态类 <br>
在src/core目录下, 新建Init.ts, 用于初始化中间件 <br></p> <ol><li>请求报文处理中间件</li></ol> <p>http请求里，报文主体（即body参数部分）是以二进制的数据在网络中进行传输，并且为了优化速度，还常常会对内容进行压缩编码，比如gzip <br>
koa-bodyparser中间件，会将post请求的请求报文进行处理，将请求主体以json格式，挂载在ctx.request.body上</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/core/Init.ts</span>

<span class="token keyword">import</span> Koa <span class="token keyword">from</span> <span class="token string">'koa'</span>
<span class="token keyword">import</span> http <span class="token keyword">from</span> <span class="token string">'http'</span>
<span class="token keyword">import</span> koaBodyParser <span class="token keyword">from</span> <span class="token string">'koa-bodyparser'</span>
<span class="token keyword">class</span> <span class="token class-name">Init</span> <span class="token punctuation">{</span>
  <span class="token keyword">public</span> <span class="token keyword">static</span> app<span class="token operator">:</span> Koa<span class="token operator">&lt;</span>Koa<span class="token punctuation">.</span>DefaultState<span class="token punctuation">,</span> Koa<span class="token punctuation">.</span>DefaultContext<span class="token operator">&gt;</span>
  <span class="token keyword">public</span> <span class="token keyword">static</span> server<span class="token operator">:</span> http<span class="token punctuation">.</span>Server
  <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token function">initCore</span><span class="token punctuation">(</span>app<span class="token operator">:</span> Koa<span class="token operator">&lt;</span>Koa<span class="token punctuation">.</span>DefaultState<span class="token punctuation">,</span> Koa<span class="token punctuation">.</span>DefaultContext<span class="token operator">&gt;</span><span class="token punctuation">,</span> server<span class="token operator">:</span> http<span class="token punctuation">.</span>Server<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    Init<span class="token punctuation">.</span>app <span class="token operator">=</span> app
    Init<span class="token punctuation">.</span>server <span class="token operator">=</span> server
    Init<span class="token punctuation">.</span><span class="token function">loadBodyParser</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// 解析body参数</span>
  <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token function">loadBodyParser</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    Init<span class="token punctuation">.</span>app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token function">koaBodyParser</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> Init<span class="token punctuation">.</span>initCore

</code></pre></div><div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/app.ts</span>
<span class="token keyword">import</span> Koa <span class="token keyword">from</span> <span class="token string">'koa'</span>
<span class="token keyword">import</span> http <span class="token keyword">from</span> <span class="token string">'http'</span>
<span class="token keyword">import</span> initCore <span class="token keyword">from</span> <span class="token string">'./core/Init'</span>
<span class="token comment">// 创建koa实例</span>
<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Koa</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 创建服务器</span>
<span class="token keyword">const</span> server<span class="token operator">:</span> http<span class="token punctuation">.</span>Server <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">http</span><span class="token punctuation">.</span><span class="token function">Server</span><span class="token punctuation">(</span>app<span class="token punctuation">.</span><span class="token function">callback</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

<span class="token comment">// 执行初始化</span>
<span class="token function">initCore</span><span class="token punctuation">(</span>app<span class="token punctuation">,</span> server<span class="token punctuation">)</span>
<span class="token comment">// 监听端口</span>
app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token number">9000</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'run success'</span><span class="token punctuation">)</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'app started at port 9000...'</span><span class="token punctuation">)</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

</code></pre></div><ol start="2"><li>路由中间件
服务器端路由，即根据不同路径的http请求做出对应的相应和处理</li></ol> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/core/Init.ts</span>
<span class="token keyword">class</span> <span class="token class-name">Init</span> <span class="token punctuation">{</span>
  <span class="token operator">...</span>
  <span class="token keyword">static</span> <span class="token keyword">async</span> <span class="token function">initLoadRouters</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    Init<span class="token punctuation">.</span>app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token punctuation">(</span>ctx<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>ctx<span class="token punctuation">.</span>path<span class="token punctuation">)</span>
      <span class="token keyword">switch</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">.</span>path<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">case</span> <span class="token string">'/login'</span><span class="token operator">:</span>
          <span class="token comment">// 只允许post请求</span>
          <span class="token keyword">if</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">.</span>method <span class="token operator">===</span> <span class="token string">'GET'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            ctx<span class="token punctuation">.</span>status <span class="token operator">=</span> <span class="token number">404</span>
            <span class="token keyword">break</span>
          <span class="token punctuation">}</span>
          ctx<span class="token punctuation">.</span>body <span class="token operator">=</span> <span class="token string">'登录成功'</span>
          <span class="token keyword">break</span>
        <span class="token keyword">case</span> <span class="token string">'/getUser'</span><span class="token operator">:</span>
          <span class="token comment">// 只允许get请求</span>
          <span class="token keyword">if</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">.</span>method <span class="token operator">===</span> <span class="token string">'POST'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            ctx<span class="token punctuation">.</span>status <span class="token operator">=</span> <span class="token number">404</span>
            <span class="token keyword">break</span>
          <span class="token punctuation">}</span>
          ctx<span class="token punctuation">.</span>body <span class="token operator">=</span> <span class="token string">'admin'</span>
          <span class="token keyword">break</span>
        <span class="token keyword">default</span><span class="token operator">:</span> <span class="token punctuation">{</span>
          ctx<span class="token punctuation">.</span>status <span class="token operator">=</span> <span class="token number">404</span>
        <span class="token punctuation">}</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
  <span class="token operator">...</span>
<span class="token punctuation">}</span>
</code></pre></div><p>但是这样做，所有的处理都在一起，在实际开发中并不合适。我们希望把不同的请求处理函数放在不同的目录和文件里, 并且能方便地设置请求路径和请求方法 <br>
koa-router是koa的一个路由中间件，它可以将请求的URL和方法（如：GET 、 POST 、 PUT 、 DELETE 等） 匹配到对应的响应程序或页面 <br></p> <p>在src下新建目录api，在api下新建目录v1，表示接口的版本，当前版本的接口都放在v1目录下 <br></p> <p>在v1下新建文件text.ts, 在这个文件里，我们创建一个路由实例</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/api/v1/test.ts</span>
<span class="token keyword">import</span> Router <span class="token keyword">from</span> <span class="token string">'koa-router'</span>
<span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Router</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  prefix<span class="token operator">:</span> <span class="token string">'/api/v1'</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

router<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/test'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  ctx<span class="token punctuation">.</span>body <span class="token operator">=</span> <span class="token string">'test'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> router

</code></pre></div><p>然后在init里加载这个路由实例</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/core/Init.ts</span>
<span class="token keyword">import</span> test <span class="token keyword">from</span> <span class="token string">'../api/v1/test'</span>
<span class="token keyword">class</span> <span class="token class-name">Init</span> <span class="token punctuation">{</span>
  <span class="token operator">...</span>
  <span class="token keyword">static</span> <span class="token keyword">async</span> <span class="token function">initLoadRouters</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    Init<span class="token punctuation">.</span>app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>test<span class="token punctuation">.</span><span class="token function">routes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
  <span class="token operator">...</span>
<span class="token punctuation">}</span>
</code></pre></div><p>目前我们约定，src/api目录下，只存放http请求路由文件，因此手动引动太过繁琐，我们希望可以自动递归遍历src/api目录，自动加载所有的路由处理 <br></p> <p>在src目录下新建common目录，用于存放公共文件，在common下新建utils目录，用于存放工具函数，再在utils下新建utils.ts <br></p> <p>我们先在utils里写一个遍历目录下所有文件默认导出的方法</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/common/utils/utils.ts</span>

<span class="token keyword">import</span> fs <span class="token keyword">from</span> <span class="token string">'fs'</span>
<span class="token keyword">import</span> path <span class="token keyword">from</span> <span class="token string">'path'</span>
<span class="token comment">/**
 * 获取某个目录下所有文件的默认导出
 * @param filePath 需要遍历的文件路径
 */</span>
<span class="token keyword">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">getAllFilesExport</span><span class="token punctuation">(</span>filePath<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> callback<span class="token operator">:</span> <span class="token builtin">Function</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 根据文件路径读取文件，返回一个文件列表</span>
  <span class="token keyword">const</span> files<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> fs<span class="token punctuation">.</span><span class="token function">readdirSync</span><span class="token punctuation">(</span>filePath<span class="token punctuation">)</span>
  <span class="token comment">// 遍历读取到的文件列表</span>
  files<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span>fileName<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token comment">// path.join得到当前文件的绝对路径</span>
    <span class="token keyword">const</span> absFilePath<span class="token operator">:</span> <span class="token builtin">string</span> <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>filePath<span class="token punctuation">,</span> fileName<span class="token punctuation">)</span>
    <span class="token keyword">const</span> stats<span class="token operator">:</span> fs<span class="token punctuation">.</span>Stats <span class="token operator">=</span> fs<span class="token punctuation">.</span><span class="token function">statSync</span><span class="token punctuation">(</span>absFilePath<span class="token punctuation">)</span>
    <span class="token keyword">const</span> isFile <span class="token operator">=</span> stats<span class="token punctuation">.</span><span class="token function">isFile</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 是否为文件</span>
    <span class="token keyword">const</span> isDir <span class="token operator">=</span> stats<span class="token punctuation">.</span><span class="token function">isDirectory</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 是否为文件夹</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>isFile<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> file <span class="token operator">=</span> <span class="token keyword">require</span><span class="token punctuation">(</span>absFilePath<span class="token punctuation">)</span>
      <span class="token function">callback</span><span class="token punctuation">(</span>file<span class="token punctuation">.</span>default<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>isDir<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token function">getAllFilesExport</span><span class="token punctuation">(</span>absFilePath<span class="token punctuation">,</span> callback<span class="token punctuation">)</span> <span class="token comment">// 递归，如果是文件夹，就继续遍历该文件夹里面的文件；</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

</code></pre></div><p>然后在init里调用</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/core/Init.ts</span>
<span class="token keyword">class</span> <span class="token class-name">Init</span> <span class="token punctuation">{</span>
  <span class="token operator">...</span>
  <span class="token keyword">static</span> <span class="token keyword">async</span> <span class="token function">initLoadRouters</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> dirPath <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>process<span class="token punctuation">.</span><span class="token function">cwd</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/src/api/</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
    <span class="token function">getAllFilesExport</span><span class="token punctuation">(</span>dirPath<span class="token punctuation">,</span> <span class="token punctuation">(</span>file<span class="token operator">:</span> Router<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      Init<span class="token punctuation">.</span>app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>file<span class="token punctuation">.</span><span class="token function">routes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
  <span class="token operator">...</span>
<span class="token punctuation">}</span>
</code></pre></div><p>此时src/api目录下的路由会被自动调用，但是还有一个问题，就是在build后，目录会变化，变成/dist/src/api/，我们需要根据环境变量，控制加载目录 <br></p> <p>其它需要使用绝对路径的地方，都会有这个问题，所以我们可以创建一个公共变量，利于复用 <br></p> <p>在src下创建目录config, 在config下创建Config.ts， 用于存放配置和公共变量</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/config/Config.ts</span>
<span class="token keyword">const</span> isDev <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span> <span class="token operator">===</span> <span class="token string">'development'</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">class</span> <span class="token class-name">Config</span> <span class="token punctuation">{</span>
  <span class="token comment">// 服务器端口</span>
  <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">readonly</span> <span class="token constant">HTTP_PORT</span> <span class="token operator">=</span> <span class="token number">9000</span>
  <span class="token comment">// 接口前缀</span>
  <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">readonly</span> <span class="token constant">API_PREFIX</span> <span class="token operator">=</span> <span class="token string">'/api/'</span>
  <span class="token comment">// 根目录</span>
  <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">readonly</span> <span class="token constant">BASE</span> <span class="token operator">=</span> isDev <span class="token operator">?</span> <span class="token string">'src'</span> <span class="token operator">:</span> <span class="token string">'dist/src'</span>
<span class="token punctuation">}</span>


</code></pre></div><p>修改app.ts</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/app.ts</span>
<span class="token keyword">import</span> Koa <span class="token keyword">from</span> <span class="token string">'koa'</span>
<span class="token keyword">import</span> http <span class="token keyword">from</span> <span class="token string">'http'</span>
<span class="token keyword">import</span> initCore <span class="token keyword">from</span> <span class="token string">'./core/Init'</span>
<span class="token keyword">import</span> Config <span class="token keyword">from</span> <span class="token string">'./config/Config'</span>
<span class="token comment">// 创建koa实例</span>
<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Koa</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 创建服务器</span>
<span class="token keyword">const</span> server<span class="token operator">:</span> http<span class="token punctuation">.</span>Server <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">http</span><span class="token punctuation">.</span><span class="token function">Server</span><span class="token punctuation">(</span>app<span class="token punctuation">.</span><span class="token function">callback</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

<span class="token comment">// 执行初始化</span>
<span class="token function">initCore</span><span class="token punctuation">(</span>app<span class="token punctuation">,</span> server<span class="token punctuation">)</span>
<span class="token comment">// 监听端口</span>
app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span>Config<span class="token punctuation">.</span><span class="token constant">HTTP_PORT</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'run success'</span><span class="token punctuation">)</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">app started at port </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>Config<span class="token punctuation">.</span><span class="token constant">HTTP_PORT</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">...</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

</code></pre></div><p>修改src/api/v1/test.ts</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/api/v1/test.ts</span>

<span class="token keyword">import</span> Router <span class="token keyword">from</span> <span class="token string">'koa-router'</span>
<span class="token keyword">import</span> Config <span class="token keyword">from</span> <span class="token string">'../../config/Config'</span>
<span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Router</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  prefix<span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>Config<span class="token punctuation">.</span><span class="token constant">API_PREFIX</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">v1</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token comment">// 路径前缀</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token comment">// 指定一个url和请求方法匹配处理</span>
router
  <span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/test'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    ctx<span class="token punctuation">.</span>body <span class="token operator">=</span> <span class="token string">'test'</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'/login'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    ctx<span class="token punctuation">.</span>body <span class="token operator">=</span> <span class="token string">'登录'</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> router

</code></pre></div><p>修改src/core/Init.ts</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/core/Init.ts</span>

<span class="token keyword">import</span> Koa <span class="token keyword">from</span> <span class="token string">'koa'</span>
<span class="token keyword">import</span> http <span class="token keyword">from</span> <span class="token string">'http'</span>
<span class="token keyword">import</span> koaBodyParser <span class="token keyword">from</span> <span class="token string">'koa-bodyparser'</span>
<span class="token keyword">import</span> path <span class="token keyword">from</span> <span class="token string">'path'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> getAllFilesExport <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../common/utils/utils'</span>
<span class="token keyword">import</span> Router <span class="token keyword">from</span> <span class="token string">'koa-router'</span>
<span class="token keyword">import</span> Config <span class="token keyword">from</span> <span class="token string">'../config/Config'</span>
<span class="token keyword">class</span> <span class="token class-name">Init</span> <span class="token punctuation">{</span>
  <span class="token keyword">public</span> <span class="token keyword">static</span> app<span class="token operator">:</span> Koa<span class="token operator">&lt;</span>Koa<span class="token punctuation">.</span>DefaultState<span class="token punctuation">,</span> Koa<span class="token punctuation">.</span>DefaultContext<span class="token operator">&gt;</span>
  <span class="token keyword">public</span> <span class="token keyword">static</span> server<span class="token operator">:</span> http<span class="token punctuation">.</span>Server
  <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token function">initCore</span><span class="token punctuation">(</span>app<span class="token operator">:</span> Koa<span class="token operator">&lt;</span>Koa<span class="token punctuation">.</span>DefaultState<span class="token punctuation">,</span> Koa<span class="token punctuation">.</span>DefaultContext<span class="token operator">&gt;</span><span class="token punctuation">,</span> server<span class="token operator">:</span> http<span class="token punctuation">.</span>Server<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    Init<span class="token punctuation">.</span>app <span class="token operator">=</span> app
    Init<span class="token punctuation">.</span>server <span class="token operator">=</span> server
    Init<span class="token punctuation">.</span><span class="token function">loadBodyParser</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    Init<span class="token punctuation">.</span><span class="token function">initLoadRouters</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// 解析body参数</span>
  <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token function">loadBodyParser</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    Init<span class="token punctuation">.</span>app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token function">koaBodyParser</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// http路由加载</span>
  <span class="token keyword">static</span> <span class="token keyword">async</span> <span class="token function">initLoadRouters</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> dirPath <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>process<span class="token punctuation">.</span><span class="token function">cwd</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>Config<span class="token punctuation">.</span><span class="token constant">BASE</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/api/</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
    <span class="token function">getAllFilesExport</span><span class="token punctuation">(</span>dirPath<span class="token punctuation">,</span> <span class="token punctuation">(</span>file<span class="token operator">:</span> Router<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      Init<span class="token punctuation">.</span>app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>file<span class="token punctuation">.</span><span class="token function">routes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> Init<span class="token punctuation">.</span>initCore

</code></pre></div><ol start="3"><li>错误监听和日志处理
koa可以通过ctx.throw()方法或者创建一个Error实例并使用throw关键字直接抛出错误, 错误会中断程序的执行.  <br>
如果错误会被try...catch捕获, 一旦被程序就会执行catch里的语句, 然后继续往下执行. <br></li></ol> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// 中间件one</span>
<span class="token keyword">const</span> <span class="token function-variable function">one</span> <span class="token operator">=</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">,</span> next<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'&gt;&gt; one'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'&lt;&lt; one'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// 中间件two</span>
<span class="token keyword">const</span> <span class="token function-variable function">two</span> <span class="token operator">=</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">,</span> next<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'&gt;&gt; two'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'&lt;&lt; two'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>one<span class="token punctuation">)</span>
app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>two<span class="token punctuation">)</span>
</code></pre></div><p>最后的打印结果是</p> <div class="language- extra-class"><pre class="language-text"><code>&gt;&gt; one
&gt;&gt; two
&lt;&lt; one
&lt;&lt; two
</code></pre></div><p>ctx的当前的上下文, next有点类型回调函数的意思, 会在执行完next后再执行下一步 <br>
因此, 在第一个中间件里, 用try...catch将next包裹, 就能监听往后所有中间件的错误, 知道给ctx.body赋值, 整个响应结束 <br>
如果我们自定义一些&quot;错误&quot;, 当捕获到不同&quot;错误&quot;时, 做出响应的处理, 那这个中间件不仅仅可以捕获异常, 还能给接口响应一个统一的出口. <br>
在src/core目录下，新建HttpException.ts，用于存放不同的http错误类型</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/core/HttpException.ts</span>
<span class="token comment">// http异常</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">HttpException</span> <span class="token keyword">extends</span> <span class="token class-name">Error</span> <span class="token punctuation">{</span>
  <span class="token keyword">public</span> message<span class="token operator">:</span> <span class="token builtin">string</span>
  <span class="token keyword">public</span> errorCode<span class="token operator">:</span> <span class="token builtin">number</span>
  <span class="token keyword">public</span> code<span class="token operator">:</span> <span class="token builtin">number</span>
  <span class="token keyword">public</span> data<span class="token operator">:</span> <span class="token builtin">any</span>
  <span class="token keyword">public</span> isBuffer <span class="token operator">=</span> <span class="token boolean">false</span>
  <span class="token keyword">public</span> responseType<span class="token operator">:</span> <span class="token builtin">string</span> <span class="token operator">|</span> <span class="token keyword">undefined</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span>data <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> msg <span class="token operator">=</span> <span class="token string">'服务器异常，请联系管理员'</span><span class="token punctuation">,</span> errorCode <span class="token operator">=</span> <span class="token number">10000</span><span class="token punctuation">,</span> code <span class="token operator">=</span> <span class="token number">400</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>message <span class="token operator">=</span> msg
    <span class="token keyword">this</span><span class="token punctuation">.</span>errorCode <span class="token operator">=</span> errorCode
    <span class="token keyword">this</span><span class="token punctuation">.</span>code <span class="token operator">=</span> code
    <span class="token keyword">this</span><span class="token punctuation">.</span>data <span class="token operator">=</span> data
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">// http参数异常</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">ParameterException</span> <span class="token keyword">extends</span> <span class="token class-name">HttpException</span> <span class="token punctuation">{</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span>msg<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> errorCode<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>code <span class="token operator">=</span> <span class="token number">422</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>message <span class="token operator">=</span> msg <span class="token operator">||</span> <span class="token string">'参数错误'</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>errorCode <span class="token operator">=</span> errorCode <span class="token operator">||</span> <span class="token number">10000</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">// http请求成功</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">Success</span> <span class="token keyword">extends</span> <span class="token class-name">HttpException</span> <span class="token punctuation">{</span>
  <span class="token keyword">public</span> data
  <span class="token keyword">public</span> responseType
  <span class="token keyword">public</span> session
  <span class="token function">constructor</span><span class="token punctuation">(</span>data<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">unknown</span><span class="token punctuation">,</span> msg <span class="token operator">=</span> <span class="token string">'ok'</span><span class="token punctuation">,</span> code <span class="token operator">=</span> <span class="token number">200</span><span class="token punctuation">,</span> errorCode <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> responseType<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> session<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>code <span class="token operator">=</span> code <span class="token comment">//200查询成功，201操作成功</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>message <span class="token operator">=</span> msg
    <span class="token keyword">this</span><span class="token punctuation">.</span>errorCode <span class="token operator">=</span> errorCode <span class="token operator">||</span> <span class="token number">0</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>data <span class="token operator">=</span> data
    <span class="token keyword">this</span><span class="token punctuation">.</span>responseType <span class="token operator">=</span> responseType
    <span class="token keyword">this</span><span class="token punctuation">.</span>session <span class="token operator">=</span> session
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">// 返回文件流</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">Buffer</span> <span class="token keyword">extends</span> <span class="token class-name">Success</span> <span class="token punctuation">{</span>
  <span class="token keyword">public</span> data
  <span class="token keyword">public</span> responseType
  <span class="token keyword">public</span> session
  <span class="token keyword">public</span> isBuffer
  <span class="token function">constructor</span><span class="token punctuation">(</span>data<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">,</span> responseType<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> session<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>code <span class="token operator">=</span> <span class="token number">200</span> <span class="token comment">//200查询成功，201操作成功</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>message <span class="token operator">=</span> <span class="token string">'ok'</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>errorCode <span class="token operator">=</span> <span class="token number">0</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>data <span class="token operator">=</span> data
    <span class="token keyword">this</span><span class="token punctuation">.</span>responseType <span class="token operator">=</span> responseType
    <span class="token keyword">this</span><span class="token punctuation">.</span>session <span class="token operator">=</span> session
    <span class="token keyword">this</span><span class="token punctuation">.</span>isBuffer <span class="token operator">=</span> <span class="token boolean">true</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">// 404</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">NotFount</span> <span class="token keyword">extends</span> <span class="token class-name">HttpException</span> <span class="token punctuation">{</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span>msg<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> errorCode<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>code <span class="token operator">=</span> <span class="token number">404</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>message <span class="token operator">=</span> msg <span class="token operator">||</span> <span class="token string">'资源未找到'</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>errorCode <span class="token operator">=</span> errorCode <span class="token operator">||</span> <span class="token number">10001</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">// 授权失败</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">AuthFailed</span> <span class="token keyword">extends</span> <span class="token class-name">HttpException</span> <span class="token punctuation">{</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span>msg<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> errorCode<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>code <span class="token operator">=</span> <span class="token number">401</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>message <span class="token operator">=</span> msg <span class="token operator">||</span> <span class="token string">'授权失败'</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>errorCode <span class="token operator">=</span> errorCode <span class="token operator">||</span> <span class="token number">10002</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">// Forbbiden</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">Forbbiden</span> <span class="token keyword">extends</span> <span class="token class-name">HttpException</span> <span class="token punctuation">{</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span>msg<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> errorCode<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>code <span class="token operator">=</span> <span class="token number">403</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>message <span class="token operator">=</span> msg <span class="token operator">||</span> <span class="token string">'禁止访问'</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>errorCode <span class="token operator">=</span> errorCode <span class="token operator">||</span> <span class="token number">100006</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">// 查询失败</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">QueryFailed</span> <span class="token keyword">extends</span> <span class="token class-name">HttpException</span> <span class="token punctuation">{</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span>msg<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> errorCode<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>code <span class="token operator">=</span> <span class="token number">500</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>message <span class="token operator">=</span> msg <span class="token operator">||</span> <span class="token string">'未查到匹配的数据'</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>errorCode <span class="token operator">=</span> errorCode <span class="token operator">||</span> <span class="token number">100006</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">// 查询失败</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">dataBaseFailed</span> <span class="token keyword">extends</span> <span class="token class-name">HttpException</span> <span class="token punctuation">{</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span>msg<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> errorCode<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>code <span class="token operator">=</span> <span class="token number">500</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>message <span class="token operator">=</span> msg <span class="token operator">||</span> <span class="token string">'数据库出错'</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>errorCode <span class="token operator">=</span> errorCode <span class="token operator">||</span> <span class="token number">100005</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

</code></pre></div><p>当请求过程中，出现一些我们预料之中的情况，比如</p> <ul><li>请求成功应该返回数据</li> <li>请求参数错误或者校验失败</li> <li>请求成功需要返回文件</li> <li>登录失效
...</li></ul> <p>我们可以抛出错误，并拦截做出处理，返回对应的状态码和数据。<br></p> <p>如果是意料之外的错误,则按异常处理, 并打印日志<br></p> <p>在src目录下新建目录middlewares，用户存放自定义中间件，然后在middlewares目录下新建catchError.ts，开发错误拦截中间件<br></p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/middlewares/catchError.ts</span>
<span class="token keyword">import</span> koa <span class="token keyword">from</span> <span class="token string">'koa'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> Success<span class="token punctuation">,</span> HttpException <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../core/HttpException'</span>
<span class="token keyword">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">catchError</span><span class="token punctuation">(</span>ctx<span class="token operator">:</span> koa<span class="token punctuation">.</span>Context<span class="token punctuation">,</span> next<span class="token operator">:</span> <span class="token builtin">Function</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> method<span class="token punctuation">,</span> path <span class="token punctuation">}</span> <span class="token operator">=</span> ctx
  <span class="token keyword">try</span> <span class="token punctuation">{</span>
    <span class="token keyword">await</span> <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>error<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 当前错误是否是我们自定义的Http错误</span>
    <span class="token keyword">const</span> isHttpException <span class="token operator">=</span> error <span class="token keyword">instanceof</span> <span class="token class-name">HttpException</span>

    <span class="token comment">// 如果不是, 则抛出错误</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>isHttpException<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      ctx<span class="token punctuation">.</span>body <span class="token operator">=</span> <span class="token punctuation">{</span>
        msg<span class="token operator">:</span> <span class="token string">'未知错误'</span><span class="token punctuation">,</span>
        errorCode<span class="token operator">:</span> <span class="token number">9999</span><span class="token punctuation">,</span>
        requestUrl<span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>method<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>path<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span>
      ctx<span class="token punctuation">.</span>status <span class="token operator">=</span> <span class="token number">500</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 如果是已知错误</span>
    <span class="token keyword">else</span> <span class="token punctuation">{</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>error<span class="token punctuation">.</span>responseType<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        ctx<span class="token punctuation">.</span>response<span class="token punctuation">.</span>type <span class="token operator">=</span> error<span class="token punctuation">.</span>responseType
      <span class="token punctuation">}</span>
      <span class="token comment">// 如果是文件流，则直接返回文件</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>error<span class="token punctuation">.</span>isBuffer<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        ctx<span class="token punctuation">.</span>body <span class="token operator">=</span> error<span class="token punctuation">.</span>data
      <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
        ctx<span class="token punctuation">.</span>body <span class="token operator">=</span> <span class="token punctuation">{</span>
          msg<span class="token operator">:</span> error<span class="token punctuation">.</span>message<span class="token punctuation">,</span>
          errorCode<span class="token operator">:</span> error<span class="token punctuation">.</span>errorCode<span class="token punctuation">,</span>
          data<span class="token operator">:</span> error<span class="token punctuation">.</span>data<span class="token punctuation">,</span>
        <span class="token punctuation">}</span>
      <span class="token punctuation">}</span>

      ctx<span class="token punctuation">.</span>status <span class="token operator">=</span> error<span class="token punctuation">.</span>code

    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>


</code></pre></div><p>然后再Init里使用中间件</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/core/Init.ts</span>
<span class="token keyword">class</span> <span class="token class-name">Init</span> <span class="token punctuation">{</span>

  <span class="token operator">...</span>
  Init<span class="token punctuation">.</span><span class="token function">initCatchError</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token operator">...</span>

  <span class="token operator">...</span>
  <span class="token comment">// 错误监听和日志处理</span>
  <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token function">initCatchError</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    Init<span class="token punctuation">.</span>app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>catchError<span class="token punctuation">)</span>
  <span class="token punctuation">}</span>

  <span class="token operator">...</span>
<span class="token punctuation">}</span>

</code></pre></div><p>我们修改下src/api/v1/test.ts接口，根据不同的请求抛出不同的处理</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/api/v1/test.ts</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> Success<span class="token punctuation">,</span> ParameterException<span class="token punctuation">,</span> AuthFailed <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../../core/HttpException'</span>
router
  <span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/test'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> <span class="token punctuation">{</span> id <span class="token punctuation">}</span> <span class="token operator">=</span> ctx<span class="token punctuation">.</span>request<span class="token punctuation">.</span>body
    <span class="token keyword">const</span> token <span class="token operator">=</span> ctx<span class="token punctuation">.</span>header<span class="token punctuation">[</span><span class="token string">'authorization'</span><span class="token punctuation">]</span> <span class="token operator">||</span> ctx<span class="token punctuation">.</span>cookies<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'authorization'</span><span class="token punctuation">)</span>
    <span class="token comment">// 如果没有携带登录信息</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>token<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">AuthFailed</span><span class="token punctuation">(</span><span class="token string">'未登录'</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 如果缺少参数或者参数类型错误</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> id <span class="token operator">!==</span> <span class="token string">'number'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ParameterException</span><span class="token punctuation">(</span><span class="token string">'缺少参数id'</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 请求成功</span>
    <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Success</span><span class="token punctuation">(</span><span class="token string">'text'</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre></div><p>打开浏览器访问<a href="http://localhost:9000/api/v1/test">http://localhost:9000/api/v1/test</a>,可以看到接口返回</p> <blockquote><p>{&quot;msg&quot;:&quot;未登录&quot;,&quot;errorCode&quot;:10002,&quot;data&quot;:{}}</p></blockquote> <p>这样当所有的错误, 以及http请求响应,都会集中在catchError中间件中处理,根据我们设置的HttpException返回对应的数据. <br></p> <p>除去控制太和返回客户端的错误信息之外,我们还需要一个日志系统,将错误记录在一个固定目录下,便于日后查看 <br></p> <p>在src/common下创建文件夹lib, 然后新建logs.ts, 用于存放日志配置.我们的日志系统主要依赖于log4js. <br></p> <p>由于日志是为了日后排查错误, 并没有记录在git版本里, 所以在服务启动时,需要先判断logs目录是否存在. <br></p> <p>log4js配置项很多, 这里只介绍了appenders和categories配置<br></p> <ul><li><p>appenders 可以在appenders添加属性设置日志类型.我们这里添加了console和date,并通过type设置记录类型.type为console时,表示记录在控制台;type为dateFile, date表示记录在文件里,并根据时间将日志分片</p></li> <li><p>categories 通过categories.defult.appenders,可以设置启用的appenders.通过categories.defult.level,可以对日志进行过滤</p></li></ul> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/common/lib/logs.ts</span>
<span class="token keyword">import</span> log4js <span class="token keyword">from</span> <span class="token string">'log4js'</span>
<span class="token keyword">import</span> fs <span class="token keyword">from</span> <span class="token string">'fs'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> isDirectory <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../utils/utils'</span>

<span class="token comment">//检查某个目录是否存在</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">isDirectory</span><span class="token punctuation">(</span><span class="token string">'logs'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 不存在则创建目录</span>
  fs<span class="token punctuation">.</span><span class="token function">mkdirSync</span><span class="token punctuation">(</span><span class="token string">'logs'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

log4js<span class="token punctuation">.</span><span class="token function">configure</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  appenders<span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token builtin">console</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      type<span class="token operator">:</span> <span class="token string">'console'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    date<span class="token operator">:</span> <span class="token punctuation">{</span>
      type<span class="token operator">:</span> <span class="token string">'dateFile'</span><span class="token punctuation">,</span>
      filename<span class="token operator">:</span> <span class="token string">'logs/date'</span><span class="token punctuation">,</span>
      category<span class="token operator">:</span> <span class="token string">'normal'</span><span class="token punctuation">,</span>
      alwaysIncludePattern<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
      pattern<span class="token operator">:</span> <span class="token string">'-yyyy-MM-dd-hh.log'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  categories<span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token keyword">default</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      appenders<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'console'</span><span class="token punctuation">,</span> <span class="token string">'date'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
      level<span class="token operator">:</span> <span class="token string">'info'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token keyword">const</span> logger <span class="token operator">=</span> log4js<span class="token punctuation">.</span><span class="token function">getLogger</span><span class="token punctuation">(</span><span class="token string">'cheese'</span><span class="token punctuation">)</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> logger

</code></pre></div><div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/common/utils/utils.ts</span>

<span class="token comment">/**
 * 判断某个文件夹是否存在
 * @param path
 * @returns {boolean}
 */</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">isDirectory</span><span class="token punctuation">(</span>path<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span>
  <span class="token keyword">try</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> stat <span class="token operator">=</span> fs<span class="token punctuation">.</span><span class="token function">statSync</span><span class="token punctuation">(</span>path<span class="token punctuation">)</span>
    <span class="token keyword">return</span> stat<span class="token punctuation">.</span><span class="token function">isDirectory</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token boolean">false</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>然后在catchError中间件里,我们引入logger</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token keyword">import</span> logger <span class="token keyword">from</span> <span class="token string">'../common/lib/logs'</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">catchError</span><span class="token punctuation">(</span>ctx<span class="token operator">:</span> koa<span class="token punctuation">.</span>Context<span class="token punctuation">,</span> next<span class="token operator">:</span> <span class="token builtin">Function</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> method<span class="token punctuation">,</span> path <span class="token punctuation">}</span> <span class="token operator">=</span> ctx
  <span class="token keyword">try</span> <span class="token punctuation">{</span>
    <span class="token keyword">await</span> <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>error<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 当前错误是否是我们自定义的Http错误</span>
    <span class="token keyword">const</span> isHttpException <span class="token operator">=</span> error <span class="token keyword">instanceof</span> <span class="token class-name">HttpException</span>

    <span class="token comment">// 如果不是, 则抛出错误</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>isHttpException<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      logger<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>method<span class="token punctuation">,</span> ctx<span class="token punctuation">.</span>response<span class="token punctuation">.</span>status<span class="token punctuation">,</span> ctx<span class="token punctuation">.</span>originalUrl<span class="token punctuation">,</span> error<span class="token punctuation">)</span>
      <span class="token operator">...</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 如果是已知错误</span>
    <span class="token keyword">else</span> <span class="token punctuation">{</span>
      <span class="token operator">...</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>error <span class="token keyword">instanceof</span> <span class="token class-name">Success</span> <span class="token operator">||</span> error <span class="token keyword">instanceof</span> <span class="token class-name">Buffer</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        logger<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span>method<span class="token punctuation">,</span> ctx<span class="token punctuation">.</span>response<span class="token punctuation">.</span>status<span class="token punctuation">,</span> ctx<span class="token punctuation">.</span>originalUrl<span class="token punctuation">)</span>
      <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
        logger<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>method<span class="token punctuation">,</span> ctx<span class="token punctuation">.</span>response<span class="token punctuation">.</span>status<span class="token punctuation">,</span> ctx<span class="token punctuation">.</span>originalUrl<span class="token punctuation">,</span> error<span class="token punctuation">)</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

</code></pre></div><p>在当我们调用接口时,就能在logs里看到日志文件date.-2022-05-03-10.log内容更新了</p> <div class="language-log extra-class"><pre class="language-log"><code><span class="token punctuation">[</span><span class="token date number">2022-05-03T</span><span class="token time number">10:09:40.680</span><span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token level error important">ERROR</span><span class="token punctuation">]</span> cheese <span class="token operator">-</span> GET <span class="token number">401</span> <span class="token file-path string">/api/v1/test?id=2</span> AuthFailed <span class="token punctuation">[</span>Error<span class="token punctuation">]</span><span class="token operator">:</span> 未登录
    at <span class="token file-path string">E:\project\git\node-project\koa-ts-learn\src\api\v1\test.ts</span><span class="token operator">:</span><span class="token number">14</span><span class="token operator">:</span><span class="token number">13</span>
    at dispatch <span class="token operator">(</span><span class="token file-path string">E:\project\git\node-project\koa-ts-learn\node_modules\koa-compose\index.js</span><span class="token operator">:</span><span class="token number">42</span><span class="token operator">:</span><span class="token number">32</span><span class="token operator">)</span>
    at <span class="token file-path string">E:\project\git\node-project\koa-ts-learn\node_modules\koa-router\lib\router.js</span><span class="token operator">:</span><span class="token number">372</span><span class="token operator">:</span><span class="token number">16</span>
    at dispatch <span class="token operator">(</span><span class="token file-path string">E:\project\git\node-project\koa-ts-learn\node_modules\koa-compose\index.js</span><span class="token operator">:</span><span class="token number">42</span><span class="token operator">:</span><span class="token number">32</span><span class="token operator">)</span>
    at <span class="token file-path string">E:\project\git\node-project\koa-ts-learn\node_modules\koa-compose\index.js</span><span class="token operator">:</span><span class="token number">34</span><span class="token operator">:</span><span class="token number">12</span>
    at dispatch <span class="token operator">(</span><span class="token file-path string">E:\project\git\node-project\koa-ts-learn\node_modules\koa-router\lib\router.js</span><span class="token operator">:</span><span class="token number">377</span><span class="token operator">:</span><span class="token number">31</span><span class="token operator">)</span>
    at dispatch <span class="token operator">(</span><span class="token file-path string">E:\project\git\node-project\koa-ts-learn\node_modules\koa-compose\index.js</span><span class="token operator">:</span><span class="token number">42</span><span class="token operator">:</span><span class="token number">32</span><span class="token operator">)</span>
    at <span class="token file-path string">E:\project\git\node-project\koa-ts-learn\src\middlewares\catchError.ts</span><span class="token operator">:</span><span class="token number">7</span><span class="token operator">:</span><span class="token number">11</span>
    at Generator<span class="token punctuation">.</span>next <span class="token operator">(</span><span class="token operator">&lt;</span>anonymous<span class="token operator">&gt;</span><span class="token operator">)</span>
    at <span class="token file-path string">E:\project\git\node-project\koa-ts-learn\src\middlewares\catchError.ts</span><span class="token operator">:</span><span class="token number">8</span><span class="token operator">:</span><span class="token number">71</span> <span class="token operator">{</span>
  <span class="token property">isBuffer:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
  <span class="token property">errorCode:</span> <span class="token number">10002</span><span class="token punctuation">,</span>
  <span class="token property">code:</span> <span class="token number">401</span><span class="token punctuation">,</span>
  <span class="token property">data:</span> undefined
<span class="token operator">}</span>

</code></pre></div><ol><li>mysql封装</li></ol> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/core/Mysql.ts</span>
<span class="token keyword">import</span> mysql <span class="token keyword">from</span> <span class="token string">'mysql'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> Models <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../common/typings/model'</span>
<span class="token keyword">import</span> dbConfig <span class="token keyword">from</span> <span class="token string">'../config/dbConfig'</span>
<span class="token keyword">import</span> httpErrors <span class="token keyword">from</span> <span class="token string">'../core/HttpException'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> lineToHumpObject <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../common/utils/Utils'</span>
<span class="token keyword">class</span> <span class="token class-name">Mysql</span> <span class="token punctuation">{</span>
  <span class="token comment">// 创建连接池</span>
  <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">readonly</span> pool <span class="token operator">=</span> mysql<span class="token punctuation">.</span><span class="token function">createPool</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    host<span class="token operator">:</span> dbConfig<span class="token punctuation">.</span>database<span class="token punctuation">.</span>host<span class="token punctuation">,</span>
    port<span class="token operator">:</span> dbConfig<span class="token punctuation">.</span>database<span class="token punctuation">.</span>port<span class="token punctuation">,</span>
    user<span class="token operator">:</span> dbConfig<span class="token punctuation">.</span>database<span class="token punctuation">.</span>username<span class="token punctuation">,</span>
    password<span class="token operator">:</span> dbConfig<span class="token punctuation">.</span>database<span class="token punctuation">.</span>password<span class="token punctuation">,</span>
    database<span class="token operator">:</span> dbConfig<span class="token punctuation">.</span>database<span class="token punctuation">.</span>dbName<span class="token punctuation">,</span>
    multipleStatements<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// 运行执行多条语句</span>
    connectionLimit<span class="token operator">:</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">,</span>
    connectTimeout<span class="token operator">:</span> <span class="token number">1000</span> <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">,</span>
    acquireTimeout<span class="token operator">:</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">,</span>
    timeout<span class="token operator">:</span> <span class="token number">1000</span> <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>

  <span class="token comment">/*
   * 数据库增删改查
   * @param command 增删改查语句
   * @param value 对应的值
   */</span>
  <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">async</span> <span class="token function">command</span><span class="token punctuation">(</span>command<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> value<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">Array</span><span class="token operator">&lt;</span><span class="token builtin">any</span><span class="token operator">&gt;</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span>Models<span class="token punctuation">.</span>Result<span class="token operator">&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">try</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name"><span class="token builtin">Promise</span><span class="token operator">&lt;</span>Models<span class="token punctuation">.</span>Result<span class="token operator">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> reject<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>pool<span class="token punctuation">.</span><span class="token function">getConnection</span><span class="token punctuation">(</span><span class="token punctuation">(</span>error<span class="token operator">:</span> mysql<span class="token punctuation">.</span>MysqlError<span class="token punctuation">,</span> connection<span class="token operator">:</span> mysql<span class="token punctuation">.</span>PoolConnection<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
          <span class="token comment">// 如果连接出错, 抛出错误</span>
          <span class="token keyword">if</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">const</span> result<span class="token operator">:</span> Models<span class="token punctuation">.</span>MysqlError <span class="token operator">=</span> <span class="token punctuation">{</span>
              error<span class="token punctuation">,</span>
              msg<span class="token operator">:</span> <span class="token string">'数据库连接出错'</span> <span class="token operator">+</span> <span class="token string">':'</span> <span class="token operator">+</span> error<span class="token punctuation">.</span>message<span class="token punctuation">,</span>
            <span class="token punctuation">}</span>
            <span class="token function">reject</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span>
          <span class="token punctuation">}</span>

          <span class="token keyword">const</span> callback<span class="token operator">:</span> mysql<span class="token punctuation">.</span><span class="token function-variable function">queryCallback</span> <span class="token operator">=</span> <span class="token punctuation">(</span>err<span class="token punctuation">,</span> results<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">,</span> fields<span class="token operator">?</span><span class="token operator">:</span> mysql<span class="token punctuation">.</span>FieldInfo<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
            connection<span class="token punctuation">.</span><span class="token function">release</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
              <span class="token keyword">const</span> result<span class="token operator">:</span> Models<span class="token punctuation">.</span>MysqlError <span class="token operator">=</span> <span class="token punctuation">{</span>
                error<span class="token operator">:</span> err<span class="token punctuation">,</span>
                msg<span class="token operator">:</span> err<span class="token punctuation">.</span>sqlMessage <span class="token operator">||</span> <span class="token string">'数据库增删改查出错'</span><span class="token punctuation">,</span>
              <span class="token punctuation">}</span>

              <span class="token function">reject</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span>
            <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
              <span class="token keyword">const</span> result<span class="token operator">:</span> Models<span class="token punctuation">.</span>Result <span class="token operator">=</span> <span class="token punctuation">{</span>
                msg<span class="token operator">:</span> <span class="token string">'ok'</span><span class="token punctuation">,</span>
                state<span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
                <span class="token comment">// 将数据库里的字段, 由下划线更改为小驼峰</span>
                results<span class="token operator">:</span> results <span class="token keyword">instanceof</span> <span class="token class-name"><span class="token builtin">Array</span></span> <span class="token operator">?</span> results<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>lineToHumpObject<span class="token punctuation">)</span> <span class="token operator">:</span> results<span class="token punctuation">,</span>
                fields<span class="token operator">:</span> fields <span class="token operator">||</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
              <span class="token punctuation">}</span>
              <span class="token function">resolve</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span>
            <span class="token punctuation">}</span>
          <span class="token punctuation">}</span>
          <span class="token comment">// 执行mysql语句</span>
          <span class="token keyword">if</span> <span class="token punctuation">(</span>value<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>pool<span class="token punctuation">.</span><span class="token function">query</span><span class="token punctuation">(</span>command<span class="token punctuation">,</span> value<span class="token punctuation">,</span> callback<span class="token punctuation">)</span>
          <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>pool<span class="token punctuation">.</span><span class="token function">query</span><span class="token punctuation">(</span>command<span class="token punctuation">,</span> callback<span class="token punctuation">)</span>
          <span class="token punctuation">}</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">httpErrors</span><span class="token punctuation">.</span><span class="token function">dataBaseFailed</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> Mysql


</code></pre></div><ol start="5"><li>登录和token</li></ol> <p>在用户登录后, 服务端创建一个令牌, 并返回给前端, 前端在请求后端时携带这个令牌, 服务端通过比对这个令牌, 就可以确定用户的身份, 避免请求中携带用户敏感信息导致的泄露, 也避免了频繁查询数据库获取用户密码造成的资源浪费.这个令牌就是token.<br>
为了保证token的安全性, 我们需要将token加密并缓存下来, 并设置过期时间</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// src/api/v1/system/auth/login.ts</span>

<span class="token keyword">import</span> <span class="token punctuation">{</span> Models <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../../../../common/typings/model'</span>
<span class="token keyword">import</span> KoaRouter <span class="token keyword">from</span> <span class="token string">'koa-router'</span>
<span class="token keyword">import</span> Mysql <span class="token keyword">from</span> <span class="token string">'../../../../core/Mysql'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> Success<span class="token punctuation">,</span> ParameterException <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../../../../core/HttpException'</span>
<span class="token keyword">import</span> validator <span class="token keyword">from</span> <span class="token string">'../../../../middlewares/validator'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> generateToken <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../../../../common/utils/Utils'</span>
<span class="token keyword">import</span> login <span class="token keyword">from</span> <span class="token string">'../../../../common/apiValidate/system/auth/login'</span>
<span class="token keyword">import</span> Config <span class="token keyword">from</span> <span class="token string">'../../../../config/config'</span>
<span class="token keyword">import</span> RedisClient <span class="token keyword">from</span> <span class="token string">'../../../../core/Redis'</span>

<span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">KoaRouter</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  prefix<span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>Config<span class="token punctuation">.</span>api_prefix<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">system/auth</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>


router<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'/login'</span><span class="token punctuation">,</span> 
  <span class="token function">validator</span><span class="token punctuation">(</span>login<span class="token punctuation">,</span> <span class="token string">'body'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">// 校验body的参数是否符合要求</span>
  checkCode<span class="token punctuation">,</span> <span class="token comment">// 校验的验证码是否正确</span>
  <span class="token keyword">async</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> <span class="token punctuation">{</span> email<span class="token punctuation">,</span> password<span class="token punctuation">,</span> userName <span class="token punctuation">}</span> <span class="token operator">=</span> ctx<span class="token punctuation">.</span>request<span class="token punctuation">.</span>body
    <span class="token keyword">const</span> res<span class="token operator">:</span> Models<span class="token punctuation">.</span>Result <span class="token operator">=</span> <span class="token keyword">await</span> Mysql<span class="token punctuation">.</span><span class="token function">command</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">
          SELECT
          id,email,deleted,info,password,role_id
          FROM
              user
          where
              user.user_name = '</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">'
      </span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
    <span class="token keyword">const</span> <span class="token punctuation">{</span>
      user<span class="token punctuation">,</span> token
    <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">getToken</span><span class="token punctuation">(</span>res<span class="token punctuation">,</span> password<span class="token punctuation">)</span>
    <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Success</span><span class="token punctuation">(</span><span class="token punctuation">{</span> user<span class="token punctuation">,</span> token <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">)</span>



<span class="token keyword">function</span> <span class="token function">getToken</span><span class="token punctuation">(</span>res<span class="token operator">:</span> Models<span class="token punctuation">.</span>Result<span class="token punctuation">,</span> password<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>res<span class="token punctuation">.</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> user <span class="token operator">=</span> res<span class="token punctuation">.</span>results<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>
    <span class="token keyword">const</span> correct <span class="token operator">=</span> <span class="token function">checkPassword</span><span class="token punctuation">(</span>password<span class="token punctuation">,</span> user<span class="token punctuation">.</span>password <span class="token keyword">as</span> <span class="token builtin">string</span><span class="token punctuation">)</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>correct<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ParameterException</span><span class="token punctuation">(</span><span class="token string">'密码不正确'</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">const</span> token <span class="token operator">=</span> <span class="token function">generateToken</span><span class="token punctuation">(</span>user<span class="token punctuation">.</span>id <span class="token keyword">as</span> <span class="token builtin">number</span><span class="token punctuation">,</span> user<span class="token punctuation">.</span>roleId<span class="token punctuation">)</span>
    RedisClient<span class="token punctuation">.</span><span class="token function">saveToken</span><span class="token punctuation">(</span>token<span class="token punctuation">,</span> user<span class="token punctuation">.</span>id<span class="token punctuation">)</span>
    <span class="token keyword">return</span> <span class="token punctuation">{</span>
      token<span class="token punctuation">,</span>
      user<span class="token punctuation">,</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>



<span class="token keyword">export</span> <span class="token keyword">default</span> router

</code></pre></div><ol start="3"><li>校验中间件开发</li></ol> <div class="language-ts extra-class"><pre class="language-ts"><code>
</code></pre></div><ol start="4"><li>给接口添加权限校验中间件.</li></ol> <p>有些接口不需要任何权限, 如登录, 有些接口需要登录权限, 就需要校验token, 有些接口需要对应的菜单权限和操作权限, 就需要校验角色权限</p> <p>后端部分主要通过mysql去存储对应的角色和权限, 并使用redis做缓存.</p> <p>主要流程:</p> <ul><li>当用户登录时保存生成token</li></ul> <h2 id="代码实现"><a href="#代码实现" class="header-anchor">#</a> 代码实现</h2> <h3 id="_1-前端-2"><a href="#_1-前端-2" class="header-anchor">#</a> 1. 前端</h3> <h3 id="_2-后端-2"><a href="#_2-后端-2" class="header-anchor">#</a> 2. 后端</h3> <ul><li>在服务启动时, 从mysql里拉取角色相关数据, 并存在redis里</li></ul> <div class="language-ts extra-class"><pre class="language-ts"><code>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">class</span> <span class="token class-name">Auth</span> <span class="token punctuation">{</span>
  <span class="token comment">/**
   * 获取用户权限
   * @param decode
   * @returns
   */</span>
  <span class="token keyword">static</span> <span class="token keyword">async</span> <span class="token function">getUserPermission</span><span class="token punctuation">(</span>decode<span class="token operator">:</span> Decode<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span>Menu<span class="token punctuation">.</span>Menu<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> <span class="token punctuation">{</span> scope <span class="token punctuation">}</span> <span class="token operator">=</span> decode
    <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name"><span class="token builtin">Promise</span></span><span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> reject<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">let</span> res<span class="token operator">:</span> Models<span class="token punctuation">.</span>Result

      <span class="token keyword">try</span> <span class="token punctuation">{</span>
        res <span class="token operator">=</span> <span class="token keyword">await</span> Mysql<span class="token punctuation">.</span><span class="token function">command</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">
            SELECT
              permissions
            FROM
              role
            where
              id = </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>scope<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">
        </span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>res<span class="token punctuation">.</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
          <span class="token keyword">const</span> user <span class="token operator">=</span> res<span class="token punctuation">.</span>results<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>
          <span class="token keyword">if</span> <span class="token punctuation">(</span>user<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">const</span> menuList<span class="token operator">:</span> Menu<span class="token punctuation">.</span>Menu<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">(</span>
              <span class="token keyword">await</span> Mysql<span class="token punctuation">.</span><span class="token function">command</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">
                SELECT
                  permission
                FROM
                  menu
                WHERE
                  FIND_IN_SET(
                  id,
                  '</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>user<span class="token punctuation">.</span>permissions<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">')
              </span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
            <span class="token punctuation">)</span><span class="token punctuation">.</span>results
            <span class="token function">resolve</span><span class="token punctuation">(</span>menuList<span class="token punctuation">)</span>
          <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
            <span class="token function">resolve</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
          <span class="token punctuation">}</span>
        <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
          <span class="token function">reject</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span>
      <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span>
        <span class="token function">reject</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
  <span class="token comment">/**
   * 获取所有角色的权限列表
   * @returns
   */</span>
  <span class="token keyword">static</span> <span class="token keyword">async</span> <span class="token function">getAllRolePermission</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span>Role<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name"><span class="token builtin">Promise</span></span><span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> reject<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">let</span> res<span class="token operator">:</span> Models<span class="token punctuation">.</span>Result
      <span class="token keyword">try</span> <span class="token punctuation">{</span>
        res <span class="token operator">=</span> <span class="token keyword">await</span> Mysql<span class="token punctuation">.</span><span class="token function">command</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">
            SELECT
              id,
              permissions,
              parent_id parentId,
              name
            FROM
              role
        </span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>res<span class="token punctuation">.</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
          <span class="token keyword">const</span> menuList<span class="token operator">:</span> Role<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
          <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> res<span class="token punctuation">.</span>results<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">const</span> item<span class="token operator">:</span> Menu<span class="token punctuation">.</span>Menu <span class="token operator">=</span> res<span class="token punctuation">.</span>results<span class="token punctuation">[</span>i<span class="token punctuation">]</span>
            menuList<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
              id<span class="token operator">:</span> item<span class="token punctuation">.</span>id<span class="token punctuation">,</span>
              parentId<span class="token operator">:</span> item<span class="token punctuation">.</span>parentId<span class="token punctuation">,</span>
              name<span class="token operator">:</span> item<span class="token punctuation">.</span>name<span class="token punctuation">,</span>
              menuList<span class="token operator">:</span> <span class="token keyword">await</span> Auth<span class="token punctuation">.</span><span class="token function">getUserPermission</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
                scope<span class="token operator">:</span> item<span class="token punctuation">.</span>id<span class="token punctuation">,</span>
                uid<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
              <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token punctuation">}</span><span class="token punctuation">)</span>
          <span class="token punctuation">}</span>
          <span class="token function">resolve</span><span class="token punctuation">(</span>menuList<span class="token punctuation">)</span>
        <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
          <span class="token function">reject</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span>
      <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token function">reject</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>

  <span class="token comment">/**
   * 更新redis里的角色
   */</span>
  <span class="token keyword">static</span> <span class="token function">updateRedisRole</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    Auth<span class="token punctuation">.</span><span class="token function">getAllRolePermission</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span>list<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      list<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span>res<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>res<span class="token punctuation">.</span>menuList<span class="token punctuation">.</span>length <span class="token operator">&gt;</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
          redisClient<span class="token punctuation">.</span><span class="token function">updateRoles</span><span class="token punctuation">(</span>
            <span class="token punctuation">(</span>res<span class="token punctuation">.</span>id <span class="token operator">||</span> <span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token keyword">new</span> <span class="token class-name">Map</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
              <span class="token punctuation">[</span><span class="token string">'id'</span><span class="token punctuation">,</span> res<span class="token punctuation">.</span>id<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
              <span class="token punctuation">[</span><span class="token string">'parentId'</span><span class="token punctuation">,</span> res<span class="token punctuation">.</span>parentId<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
              <span class="token punctuation">[</span><span class="token string">'permissions'</span><span class="token punctuation">,</span> res<span class="token punctuation">.</span>menuList<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span>item<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> item<span class="token punctuation">.</span>permission<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">','</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
            <span class="token punctuation">]</span><span class="token punctuation">)</span>
          <span class="token punctuation">)</span>
        <span class="token punctuation">}</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><ul><li>权限校验中间件开发</li></ul> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">class</span> <span class="token class-name">Auth</span> <span class="token punctuation">{</span>
  <span class="token comment">/**
   * 校验token
   * @param ctx
   * @param next
   * @param callback
   */</span>
  <span class="token keyword">static</span> <span class="token keyword">async</span> <span class="token function">verifyToken</span><span class="token punctuation">(</span>ctx<span class="token operator">:</span> Models<span class="token punctuation">.</span>Ctx<span class="token punctuation">,</span> next<span class="token operator">:</span> Koa<span class="token punctuation">.</span>Next<span class="token punctuation">,</span> callback<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">Function</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">//检测token是否合法</span>
    <span class="token keyword">const</span> userToken <span class="token operator">=</span> Auth<span class="token punctuation">.</span><span class="token function">getToken</span><span class="token punctuation">(</span>ctx<span class="token punctuation">)</span>
    <span class="token keyword">let</span> errMsg <span class="token operator">=</span> <span class="token string">'token不合法'</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>userToken<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Forbbiden</span><span class="token punctuation">(</span><span class="token string">'无访问权限'</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">const</span> uid <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">await</span> redisClient<span class="token punctuation">.</span><span class="token function">getTokenValue</span><span class="token punctuation">(</span>userToken<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>results
    <span class="token keyword">let</span> decode
    <span class="token keyword">try</span> <span class="token punctuation">{</span>
      <span class="token comment">// 解析token</span>
      decode <span class="token operator">=</span> jwt<span class="token punctuation">.</span><span class="token function">verify</span><span class="token punctuation">(</span>userToken<span class="token punctuation">,</span> config<span class="token punctuation">.</span>security<span class="token punctuation">.</span>secretKey<span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token builtin">string</span> <span class="token operator">|</span> Decode
    <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>error <span class="token operator">&amp;&amp;</span> <span class="token punctuation">(</span>error <span class="token keyword">as</span> Error<span class="token punctuation">)</span><span class="token operator">?.</span>name <span class="token operator">==</span> <span class="token string">'TokenExpiredError'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        errMsg <span class="token operator">=</span> <span class="token string">'token已过期'</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">AuthFailed</span><span class="token punctuation">(</span>errMsg<span class="token punctuation">)</span>
      <span class="token punctuation">}</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Forbbiden</span><span class="token punctuation">(</span>errMsg<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> decode <span class="token operator">===</span> <span class="token string">'object'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>uid <span class="token operator">!=</span> decode<span class="token punctuation">.</span>uid<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        errMsg <span class="token operator">=</span> <span class="token string">'token已过期'</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">AuthFailed</span><span class="token punctuation">(</span>errMsg<span class="token punctuation">,</span> <span class="token number">401</span><span class="token punctuation">)</span>
      <span class="token punctuation">}</span>
      <span class="token comment">// 保存uid和scope</span>
      ctx<span class="token punctuation">.</span>auth <span class="token operator">=</span> <span class="token punctuation">{</span>
        uid<span class="token operator">:</span> decode<span class="token punctuation">.</span>uid<span class="token punctuation">,</span>
        scope<span class="token operator">:</span> decode<span class="token punctuation">.</span>scope<span class="token punctuation">,</span>
      <span class="token punctuation">}</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>callback<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">await</span> <span class="token function">callback</span><span class="token punctuation">(</span>decode<span class="token punctuation">)</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
      errMsg <span class="token operator">=</span> <span class="token string">'权限不足'</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Forbbiden</span><span class="token punctuation">(</span>errMsg<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">await</span> <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>

  <span class="token comment">/**
   * 获取token
   * @param ctx
   * @returns
   */</span>
  <span class="token keyword">static</span> <span class="token function">getToken</span><span class="token punctuation">(</span>ctx<span class="token operator">:</span> Models<span class="token punctuation">.</span>Ctx<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> ctx<span class="token punctuation">.</span>header<span class="token punctuation">[</span><span class="token string">'authorization'</span><span class="token punctuation">]</span> <span class="token operator">||</span> ctx<span class="token punctuation">.</span>cookies<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'authorization'</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>

  <span class="token comment">/**
   * 校验token权限
   * @param ctx
   * @param next
   */</span>
  <span class="token keyword">static</span> <span class="token keyword">async</span> <span class="token function">verifyTokenPermission</span><span class="token punctuation">(</span>ctx<span class="token operator">:</span> Models<span class="token punctuation">.</span>Ctx<span class="token punctuation">,</span> next<span class="token operator">:</span> Koa<span class="token punctuation">.</span>Next<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">await</span> Auth<span class="token punctuation">.</span><span class="token function">verifyToken</span><span class="token punctuation">(</span>ctx<span class="token punctuation">,</span> next<span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span>decode<span class="token operator">:</span> Decode<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> permissionList<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">await</span> Auth<span class="token punctuation">.</span><span class="token function">getRedisUserPermission</span><span class="token punctuation">(</span>decode<span class="token punctuation">)</span>

      <span class="token keyword">const</span> bool <span class="token operator">=</span> permissionList<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">(</span>permission<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> path <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>config<span class="token punctuation">.</span>api_prefix<span class="token interpolation-punctuation punctuation">}</span></span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>permission<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">':'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
        <span class="token keyword">return</span> path <span class="token operator">===</span> ctx<span class="token punctuation">.</span>path
      <span class="token punctuation">}</span><span class="token punctuation">)</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>bool<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Forbbiden</span><span class="token punctuation">(</span><span class="token string">'权限不足'</span><span class="token punctuation">)</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><ul><li>为接口添加校验中间件</li></ul> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">KoaRouter</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  prefix<span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>Config<span class="token punctuation">.</span>api_prefix<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">system/user</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token comment">/**
 * 编辑密码
 */</span>
router<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span>
  <span class="token string">'/editPassword'</span><span class="token punctuation">,</span> 
  Auth<span class="token punctuation">.</span>verifyTokenPermission<span class="token punctuation">,</span> 
  <span class="token function">validator</span><span class="token punctuation">(</span>editPassword<span class="token punctuation">,</span> <span class="token string">'body'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  checkCode<span class="token punctuation">,</span>
  <span class="token keyword">async</span> <span class="token punctuation">(</span>ctx<span class="token operator">:</span> Models<span class="token punctuation">.</span>Ctx<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> id <span class="token operator">=</span> ctx<span class="token punctuation">.</span>auth<span class="token operator">?.</span>uid
    <span class="token keyword">const</span> <span class="token punctuation">{</span> password<span class="token punctuation">,</span> emailCode <span class="token punctuation">}</span> <span class="token operator">=</span> ctx<span class="token punctuation">.</span>request<span class="token punctuation">.</span>body
    <span class="token keyword">if</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">.</span>session<span class="token operator">!</span><span class="token punctuation">.</span>emailCode <span class="token operator">!==</span> emailCode<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ParameterException</span><span class="token punctuation">(</span><span class="token string">'邮箱验证码错误'</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">await</span> Mysql<span class="token punctuation">.</span><span class="token function">command</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">
      UPDATE
        user
      SET password = '</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>password<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">'
      WHERE id = </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>id<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">
  </span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
    <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Success</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">)</span>

</code></pre></div><h4 id="_2-目录结构"><a href="#_2-目录结构" class="header-anchor">#</a> 2. 目录结构</h4> <p>src下
+ api 接口目录
+ common</p> <h5 id="校验流程"><a href="#校验流程" class="header-anchor">#</a> 校验流程</h5> <ul><li>服务启动时, 查询mysql数据库查询所有角色并存入redis里</li> <li>当用户登录后, 后端根据用户的id和角色id生成token保存在redis里, 并设置过期时间, 然后返回给前端</li> <li>给需要校验的接口加上校验中间件</li> <li>首先去redis里校验请求里携带的token是否有效, 无效则返回401. 有效则继续在查询redis里角色数据, 并解析token拿到角色id, 来获取</li></ul></div></section> <footer class="page-edit"><!----> <!----></footer> <!----> <div class="comments-wrapper"><!----></div> <ul class="side-bar sub-sidebar-wrapper" style="width:0;" data-v-cb1513f6></ul></main> <!----></div></div></div></div><div class="global-ui"><div class="back-to-ceiling" style="right:1rem;bottom:6rem;width:2.5rem;height:2.5rem;border-radius:.25rem;line-height:2.5rem;display:none;" data-v-c6073ba8 data-v-c6073ba8><svg t="1574745035067" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5404" class="icon" data-v-c6073ba8><path d="M526.60727968 10.90185116a27.675 27.675 0 0 0-29.21455937 0c-131.36607665 82.28402758-218.69155461 228.01873535-218.69155402 394.07834331a462.20625001 462.20625001 0 0 0 5.36959153 69.94390903c1.00431239 6.55289093-0.34802892 13.13561351-3.76865779 18.80351572-32.63518765 54.11355614-51.75690182 118.55860487-51.7569018 187.94566865a371.06718723 371.06718723 0 0 0 11.50484808 91.98906777c6.53300375 25.50556257 41.68394495 28.14064038 52.69160883 4.22606766 17.37162448-37.73630017 42.14135425-72.50938081 72.80769204-103.21549295 2.18761121 3.04276886 4.15646224 6.24463696 6.40373557 9.22774369a1871.4375 1871.4375 0 0 0 140.04691725 5.34970492 1866.36093723 1866.36093723 0 0 0 140.04691723-5.34970492c2.24727335-2.98310674 4.21612437-6.18497483 6.3937923-9.2178004 30.66633723 30.70611158 55.4360664 65.4791928 72.80769147 103.21549355 11.00766384 23.91457269 46.15860503 21.27949489 52.69160879-4.22606768a371.15156223 371.15156223 0 0 0 11.514792-91.99901164c0-69.36717486-19.13165746-133.82216804-51.75690182-187.92578088-3.42062944-5.66790279-4.76302748-12.26056868-3.76865837-18.80351632a462.20625001 462.20625001 0 0 0 5.36959269-69.943909c-0.00994388-166.08943902-87.32547796-311.81420293-218.6915546-394.09823051zM605.93803103 357.87693858a93.93749974 93.93749974 0 1 1-187.89594924 6.1e-7 93.93749974 93.93749974 0 0 1 187.89594924-6.1e-7z" p-id="5405" data-v-c6073ba8></path><path d="M429.50777625 765.63860547C429.50777625 803.39355007 466.44236686 1000.39046097 512.00932183 1000.39046097c45.56695499 0 82.4922232-197.00623328 82.5015456-234.7518555 0-37.75494459-36.9345906-68.35043303-82.4922232-68.34111062-45.57627738-0.00932239-82.52019037 30.59548842-82.51086798 68.34111062z" p-id="5406" data-v-c6073ba8></path></svg></div></div></div>
    <script src="/assets/js/app.8ca67ad1.js" defer></script><script src="/assets/js/3.dc5e7bc7.js" defer></script><script src="/assets/js/1.f19b5812.js" defer></script><script src="/assets/js/42.b0eb455d.js" defer></script>
  </body>
</html>
